package zheezes.swt.databinding;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

final class ListenerSupport {
	private final PropertyChangeListener listener;
	private final List<String> properties;
	private final Set<IdentityWrapper> elementsListenedTo = new HashSet<IdentityWrapper>();
	
	public ListenerSupport(final PropertyChangeListener lsnr, List<String> list) {
		properties = list;
		listener = new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent event) {
				if (properties.contains(event.getPropertyName())) {
					lsnr.propertyChange(event);
				}
			}
		};
	}
	
	public void hookListener(Object addedElement) {
		if (processListener("addPropertyChangeListener", "Could not attach listener to ", addedElement)) {
			elementsListenedTo.add(new IdentityWrapper(addedElement));
		}
	}
	public void unhookListener(Object removedElement) {
		if (removedElement.getClass() == IdentityWrapper.class) {
			IdentityWrapper wrapper = (IdentityWrapper) removedElement;
			removedElement = wrapper.unwrap();
		}
		if (processListener("removePropertyChangeListener", "Cound not remove listener from ", removedElement)) {
			elementsListenedTo.remove(new IdentityWrapper(removedElement));
		}
	}
	public void dispose() {
		for (IdentityWrapper wrapper : elementsListenedTo) {
			if (processListener("removePropertyChangeListener",
				"Cound not remove listener from ",
				wrapper.unwrap())) {
				elementsListenedTo.remove(wrapper);
			}
		}
		elementsListenedTo.clear();
	}
	
	private boolean processListener(String methodName, String message, Object target) {
		Method method = null;
		int parameters = 0;
		try {
			try {
				method =
						target.getClass().getMethod(methodName,
							new Class[]{String.class, PropertyChangeListener.class});
				parameters = 2;
			} catch (NoSuchMethodException e) {
				method = target.getClass().getMethod(methodName, new Class[]{PropertyChangeListener.class});
				parameters = 1;
			}
		} catch (SecurityException e) {
		} catch (NoSuchMethodException e) {
			throw new RuntimeException(message, e);
		}
		if (method != null && parameters != 0) {
			if (!method.isAccessible()) {
				method.setAccessible(true);
			}
			try {
				if (parameters == 1) {
					method.invoke(target, new Object[]{listener});
				} else {
					for (String propertyName : properties) {
						method.invoke(target, new Object[]{propertyName, listener});
					}
				}
				return true;
			} catch (Exception e) {
				throw new RuntimeException(message, e);
			}
		}
		return false;
	}
}